"""collect desc info from locust files"""

import json
import os
import re
from typing import Dict, List

import yaml
import requests

from lib.exception import CustomException
from lib.env_manager import git_pull
from lib.global_share import GlobalShare, error_note_taker
from lib.logger import logger

# pylint:disable=W0718:broad-exception-caught
# pylint:disable=C0301:line-too-long


def zenki_collect_and_report_locust_cases(repo_name: str, env_name: str):
    """收集并上报仓库中的 locust 用例"""

    repo_local_path, repo_git_url = git_pull(repo_name=repo_name)
    if not repo_local_path:
        raise CustomException(f"repo {repo_name} does not exist.")

    try:
        cases_info = collect_locust_cases_info(
            repo_dir_path=f"{repo_local_path}/{repo_name}/test_case",
            repo_git_url=repo_git_url,
            cut_path=f"{repo_local_path}/",
        )
    except Exception as e:
        error_note_taker.error(str(e))

    report_to_peqa(locust_cases_info=cases_info, env=env_name)


def iter_locust_files(dir_path: str):
    """遍历 locust 文件"""

    for root, _, files in os.walk(dir_path):
        for file in files:
            if file.endswith(".py"):
                py_path = os.path.join(root, file)
                with open(py_path, "r", encoding="utf-8") as f:
                    py_content = f.read()
                    if "locust" in py_content:
                        yield py_path, py_content


def collect_locust_cases_info(
    repo_dir_path: str, repo_git_url: str, cut_path: str
) -> List[Dict[str, str]]:
    """收集 locust 用例信息

    Args:
        repo_dir_path (str): Directory path of the repository.
        repo_git_url (str): Git URL of the repository.
        cut_path (str): Path to be cut from the Locust file path.

    Returns:
        locust_cases_info (list): List containing Locust cases information.
            - repo_git_url (str): Git URL of the repository.
            - locust_file_rel_path (str): Relative path of the Locust file (relative to the cut path).
            - locust_file_desc (str): Description of the Locust file.
            - server_api_map (dict): Map of server names and list of API paths.
            - host (str): Host address.
            - case_owner (str): Owner of the test case.
    """

    locust_cases_info = []

    for locust_file_path, _ in iter_locust_files(repo_dir_path):
        module_docstring = extract_module_docstring(locust_file_path)
        if not module_docstring:
            continue
        desc_info = parse_yaml(module_docstring)
        if not isinstance(desc_info, dict):
            raise CustomException(
                f"the yaml description of the Locust file {locust_file_path} parse into json failed"
            )
        if "desc" not in desc_info:
            raise CustomException(
                f"the locust file {locust_file_path} does not have the desc field"
            )
        if "server_api_map" not in desc_info:
            raise CustomException(
                f"the locust file {locust_file_path} does not have the server_api_map field"
            )
        if not isinstance(desc_info["server_api_map"], dict):
            raise CustomException(
                f"the locust file {locust_file_path} server_api_map field is not a dictionary"
            )
        for key, value in desc_info["server_api_map"].items():
            if not isinstance(key, str):
                raise CustomException(
                    f"the locust file {locust_file_path} server_api_map field key {key} is not a string"
                )
            if not isinstance(value, list):
                raise CustomException(
                    f"the locust file {locust_file_path} server_api_map field is not a dictionary of lists, invalid key({key}) value: {value}"
                )
        if "owner" not in desc_info:
            raise CustomException(
                f"the locust file {locust_file_path} does not have the owner field"
            )
        if "host" not in desc_info:
            raise CustomException(
                f"the locust file {locust_file_path} does not have the host field"
            )
        locust_case_info = {
            "repo_git_url": repo_git_url,
            "locust_file_rel_path": locust_file_path[len(cut_path) :],
            "locust_file_desc": desc_info["desc"],
            "server_api_map": desc_info["server_api_map"],
            "case_owner": desc_info["owner"],
            "host": desc_info["host"],
        }
        locust_cases_info.append(locust_case_info)

    return locust_cases_info


def report_to_peqa(locust_cases_info, env="dev"):
    """
    Sends a report by making a POST request to a specific URL with JSON data and logs the response.

    Args:
    - data: The data to be sent as JSON in the request body.
    - env: The environment to which the request is made. Defaults to "dev".

    Returns:
    - None

    Raises:
    - Any exceptions raised during the request or response handling.
    """

    url = f"https://jtl.jidu{env}.com/api/peqacenter-tools/pressure/test_case/upload"

    data = json.dumps(locust_cases_info)
    try:
        response = requests.post(
            url,
            headers={"Content-Type": "application/json"},
            data=data,
            timeout=5,
        )
        logger.debug(response.text)
        json_r = response.json()
        if json_r["code"] != 0:
            logger.debug(json_r)
        return json_r
    except Exception as exc:
        logger.error(exc)
        raise exc


def extract_module_docstring(file_path: str) -> str:
    """
    Extracts the docstring at the top of a Python file.

    Args:
    - file_path: Path to the Python file.

    Returns:
    - The docstring at the top of the file, or an empty string if there is no docstring.
    """

    with open(file_path, "r", encoding="utf-8") as file:
        content = file.read()

    pattern = r'"""CASE_INFO(.*?)"""'
    match = re.search(pattern, content, re.DOTALL)

    if match:
        case_info = match.group(1).strip()
        return case_info
    else:
        return None


def parse_yaml(yaml_content: str) -> dict:
    """
    转换 YAML 内容为字典

    Args:
    - yaml_content: YAML 格式内容.

    Returns:
    - dict: YAML 内容转换后的字典.
    """

    try:
        _data = yaml.safe_load(yaml_content)
        return _data
    except yaml.YAMLError as e:
        raise CustomException("safe load yaml content failed") from e


def has_locust_import(file_path):
    """判断文件中是否引用了 locust"""

    with open(file_path, "r", encoding=GlobalShare.ENCODING_UTF8) as file:
        file_content = file.read()
        if "import locust" in file_content:
            return True
        if "from locust" in file_content:
            return True

    return False


def collect_locust_files(directory_path):
    """收集 locust 文件"""

    result = {}

    for root, _, files in os.walk(directory_path):
        relative_path = os.path.relpath(root, directory_path)
        if relative_path.startswith(".git"):
            continue

        file_list = []
        for file_name in files:
            if not file_name.endswith(".py"):
                continue
            file_path = os.path.join(root, file_name)
            if not has_locust_import(file_path):
                continue
            file_list.append(file_name)

        if file_list:
            result[relative_path] = file_list

    return result
